package Parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.TreeMap;

import org.supercsv.io.CsvMapWriter;
import org.supercsv.io.ICsvMapWriter;
import org.supercsv.prefs.CsvPreference;

public class MetricsParser {

	private final String nameMetric = "<Metric";
	private final String description = "description =";
	private final String nameValue = "<Valuename=";
	private final String packageName = "package =";
	private final String value = "value =";
	public final int noExist = -1;
	public final String maximum = "(MAX)";
	public static final String PACK = "Package";
	public static final String WMC = "Weighted methods per Class";
	public static final String SI = "Specialization Index";
	public static final String NSM = "Number of Static Methods";
	public static final String NSA = "Number of Static Attributes";
	public static final String NP = "Number of Parameters";
	public static final String NOM = "Number of Overridden Methods";
	public static final String NM = "Number of Methods";
	public static final String NI = "Number of Interfaces";
	public static final String NClasses = "Number of Classes";
	public static final String NChildren = "Number of Children";
	public static final String NA = "Number of Attributes";
	public static final String ND = "Normalized Distance";
	public static final String NBD = "Nested Block Depth";
	public static final String MLC = "Method Lines of Code";
	public static final String MCC = "McCabe Cyclomatic Complexity";
	public static final String LCM = "Lack of Cohesion of Methods";
	public static final String I = "Instability";
	public static final String EC = "Efferent Coupling";
	public static final String DIT = "Depth of Inheritance Tree";
	public static final String AC = "Afferent Coupling";
	public static final String A = "Abstractness";

	private boolean isMetric(String line) {
		return line.indexOf(nameMetric) != noExist;
	}

	private boolean isNameValue(String line) {
		return line.replaceAll(" ", "").startsWith(nameValue);
	}

	private String getNameMetric(String line) throws Exception {
		if (!isMetric(line)) {
			throw new Exception("No name value, no package.");
		}
		int index2 = (line.substring(line.indexOf(description)
				+ description.length() + 1)).indexOf("\"");
		return line.substring(line.indexOf(description) + description.length()
				+ 1, line.indexOf(description) + description.length() + 1
				+ index2);

	}

	private String getPackage(String line) throws Exception {
		if (!isNameValue(line)) {
			throw new Exception("No name value, no package.");
		}
		int index2 = (line.substring(line.indexOf(packageName)
				+ packageName.length() + 1)).indexOf("\"");
		String name = line.substring(
				line.indexOf(packageName) + packageName.length() + 1,
				line.indexOf(packageName) + packageName.length() + 1 + index2);
		int limit = 50 - name.length();
		for (int i = 0; i <= limit; i++) {
			name = name.concat(" ");
		}

		return name;
	}

	private Double getValue(String line) throws Exception {
		if (!isNameValue(line)) {
			throw new Exception("No name value, no value.");
		}
		int index2 = (line.substring(line.indexOf(value) + value.length() + 1))
				.indexOf("\"");
		return Double.parseDouble((line.substring(
				line.indexOf(value) + value.length() + 1, line.indexOf(value)
						+ value.length() + 1 + index2)).replace(",", "."));
	}

	public void extractAndGenerate(String metricsPath, String generatedPath) {
		try {
			writeMetrics(changeToCSV(extractMetrics(metricsPath)),
					generatedPath);
		} catch (Exception e) {
			System.out.println(e);
		}
	}

	private void writeMetrics(TreeMap<String, TreeMap<String, Double>> packs,
			String path) throws IOException {
		File file = new File(path);
		file.createNewFile();
		FileWriter fileWriter = new FileWriter(file.getAbsolutePath());
		ICsvMapWriter writer = new CsvMapWriter(fileWriter,
				CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE);

		final String[] header = new String[] { PACK, WMC, WMC + maximum, SI,
				NSM, NSA, NP, NP + maximum, NOM, NM, NM + maximum, NI,
				NClasses, NChildren, NA + maximum, ND, NBD, NBD + maximum, MLC,
				MLC + maximum, MCC + maximum, LCM, LCM + maximum, I, EC,
				DIT + maximum, AC, A };

		writer.writeHeader(header);

		for (String pack : packs.keySet()) {
			HashMap<String, ? super Object> data = new HashMap<String, Object>();
			data.put(header[0], pack);
			TreeMap<String, Double> auxMetrics = packs.get(pack);
			for (int i = 1; i < header.length; i++) {
				if (auxMetrics.get(header[i]) == null) {
					data.put(header[i], "0");
				} else {
					data.put(header[i], auxMetrics.get(header[i]).toString());
				}
			}
			writer.write(data, header);
		}
		writer.close();
	}

	public TreeMap<String, TreeMap<String, Double>> extractMetrics(String path)
			throws Exception {
		TreeMap<String, TreeMap<String, Double>> metrics = new TreeMap<String, TreeMap<String, Double>>();
		File file = new File(path);
		FileReader fileReader = new FileReader(file);
		BufferedReader input = new BufferedReader(fileReader);
		String line = null;
		String auxiliarMetric = null;
		while ((line = input.readLine()) != null) {
			if (isMetric(line)) {
				if (getNameMetric(line).equalsIgnoreCase(WMC)
						|| getNameMetric(line).equalsIgnoreCase(NP)
						|| getNameMetric(line).equalsIgnoreCase(NM)
						|| getNameMetric(line).equalsIgnoreCase(NBD)
						|| getNameMetric(line).equalsIgnoreCase(MLC)
						|| getNameMetric(line).equalsIgnoreCase(LCM)
						|| getNameMetric(line).equalsIgnoreCase(NA)
						|| getNameMetric(line).equalsIgnoreCase(MCC)
						|| getNameMetric(line).equalsIgnoreCase(DIT)) {
					TreeMap<String, Double> metric = new TreeMap<String, Double>();
					metrics.put(getNameMetric(line) + this.maximum, metric);
				}

				if (!(getNameMetric(line).equalsIgnoreCase(NA)
						&& getNameMetric(line).equalsIgnoreCase(MCC) && getNameMetric(
							line).equalsIgnoreCase(DIT))) {
					TreeMap<String, Double> metric = new TreeMap<String, Double>();
					metrics.put(getNameMetric(line), metric);

				}
				auxiliarMetric = getNameMetric(line);
			} else if (isNameValue(line)) {
				if (metrics.get(auxiliarMetric).containsKey(getPackage(line))
						|| metrics.get(auxiliarMetric).containsKey(
								getPackage(line) + maximum)) {
					if (auxiliarMetric.equalsIgnoreCase(WMC)
							|| auxiliarMetric.equalsIgnoreCase(NP)
							|| auxiliarMetric.equalsIgnoreCase(NM)
							|| auxiliarMetric.equalsIgnoreCase(NBD)
							|| auxiliarMetric.equalsIgnoreCase(MLC)
							|| auxiliarMetric.equalsIgnoreCase(LCM)
							|| auxiliarMetric.equalsIgnoreCase(NA)
							|| auxiliarMetric.equalsIgnoreCase(MCC)
							|| auxiliarMetric.equalsIgnoreCase(DIT)) {

						metrics.get(auxiliarMetric + this.maximum).put(
								getPackage(line),
								Math.max(
										getValue(line),
										metrics.get(
												auxiliarMetric + this.maximum)
												.get(getPackage(line))));
					}

					if (!(auxiliarMetric.equalsIgnoreCase(NA)
							&& auxiliarMetric.equalsIgnoreCase(MCC) && auxiliarMetric
								.equalsIgnoreCase(DIT))) {
						double total = metrics.get(auxiliarMetric).get(
								getPackage(line))
								+ getValue(line);
						metrics.get(auxiliarMetric)
								.put(getPackage(line), total);
					}

				} else {
					if (auxiliarMetric.equalsIgnoreCase(WMC)
							|| auxiliarMetric.equalsIgnoreCase(NP)
							|| auxiliarMetric.equalsIgnoreCase(NM)
							|| auxiliarMetric.equalsIgnoreCase(NBD)
							|| auxiliarMetric.equalsIgnoreCase(MLC)
							|| auxiliarMetric.equalsIgnoreCase(LCM)
							|| auxiliarMetric.equalsIgnoreCase(NA)
							|| auxiliarMetric.equalsIgnoreCase(MCC)
							|| auxiliarMetric.equalsIgnoreCase(DIT)) {

						metrics.get(auxiliarMetric + this.maximum).put(
								getPackage(line), getValue(line));

					}
					if (!auxiliarMetric.equalsIgnoreCase(NA)
							|| !auxiliarMetric.equalsIgnoreCase(MCC)
							|| !auxiliarMetric.equalsIgnoreCase(DIT)) {
						metrics.get(auxiliarMetric).put(getPackage(line),
								getValue(line));
					}
				}

			}
		}
		return metrics;
	}

	public TreeMap<String, TreeMap<String, Double>> changeToCSV(
			TreeMap<String, TreeMap<String, Double>> metrics) {
		TreeMap<String, TreeMap<String, Double>> packs = new TreeMap<String, TreeMap<String, Double>>();
		for (String metric : metrics.keySet()) {
			TreeMap<String, Double> packAux = metrics.get(metric);
			for (String packName : packAux.keySet()) {
				if (!packs.containsKey(packName)) {
					TreeMap<String, Double> metricAux = new TreeMap<String, Double>();
					metricAux.put(metric, packAux.get(packName));
					packs.put(packName, metricAux);
				} else {
					packs.get(packName).put(metric, packAux.get(packName));
				}
			}
		}
		return packs;
	}

	public static void main(String[] args) {
		MetricsParser mp = new MetricsParser();
		mp.extractAndGenerate("Files/Metrics",
				"Files/CodesMetrics.csv");
		try {
			// mp.extractMetrics("/Users/natamelo/IAProject/Parser/Files/Metrics");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
